/** @file cam-target.c
 * Implementiert die Kamera-Schnittstelle auf dem Zielsystem (LCV-Board) 
 * 
 * @author Daniel Kummer, Sebastian Kummer
 * @date Donnerstag, Dezember 12, 2007
 * @version 1.0
 */

#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <strings.h> 
#include "cam.h"
#include "cam-reg-config.h"
#include "i2c.h"

//Bayer Conversion Filter
#include "bayer-lcv.h"
#include "bmp-header.h"


/**
 *  Buffer initialisieren
 */
void lcv_cam_registers_buffer_init();
/**
 * Grösse des Buffers
 */
unsigned short cam_registers_buffer_cnt = sizeof(cam_reg_config) / sizeof(*cam_reg_config);

/**
 * Bufferarray für Kameraregister
 */
unsigned short cam_registers_buffer[sizeof(cam_reg_config) / sizeof(*cam_reg_config)];

/**
 * Buffer initialisiert
 */
unsigned char cam_registers_buffer_initialized = FALSE;

/**
 * Buffer aktivieren, deaktivieren
 */
unsigned char enable_buffer = TRUE;

/*
 * Wandelt 10 Bit Pixel Bilder (2 Byte Pro Pixel) in 1 Bit Pixel Bilder (1 Byte pro Pixel
 * @param pic Bildarray
 * @param size Bildgrösse
 */
void Raw10To8bit(unsigned char* pic, int size)
{
	int i;
	unsigned char msb, lsb, new;

	for (i=0;i<size;i++)
	{	
		msb = (unsigned char) *(pic+2*i+1);
		lsb = (unsigned char) *(pic+2*i);
		new = (unsigned char)((msb<<6) | (lsb&0x00ff)>>2);
		memset(pic+i, new, 1);
	}
}

lcv_picture lcv_capture_frame(const char* filename)
{	 
	unsigned char* buffer;
	unsigned char* bgr_buffer;	 
	int cam_dev;
	int i, imagesize;
	unsigned short width, height;
	char* error_msg;  
	lcv_picture res_pic;
	
	unsigned int beyer_offset = 1078;
		
	 
	/* Prüfe ob die Bildgrösse im gültigen Bereich liegt */
	/* Achtung: hier wurden für die Bildausmasse die Default Werte verwendet, die möglichen Werte gehen
	 * über den möglichen Bereich heraus!! */
	width  = lcv_config_read(REG_WINDOW_WIDTH);
	height = lcv_config_read(REG_WINDOW_HEIGHT);
		
	if (width > cam_reg_config[REG_WINDOW_WIDTH].max_val) {
		error_msg = "The captured picture extends the maximal possible window width";		
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1);
	}
	
	if (height > cam_reg_config[REG_WINDOW_HEIGHT].max_val) {
		error_msg = "The captured picture extends the maximal possible window height";		
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1);
	}
	 
	imagesize = width*height;
	 	 
	/* Speicher für Rohdaten und Bitmapheader alloziieren */
	buffer = (unsigned char *)malloc(2*imagesize + beyer_offset); //2 byte per pixel

	/* Kamera öffnen */
	cam_dev = open(CAM_DEVICE, O_RDONLY, 0);
	if (cam_dev == -1) {
		free(buffer);
		exit(-1); /*schwerer Fehler */
	}
	
	/* Rohe Bilddaten von PPI lesen */	
	/* ACHTUNG!! Schleife entfernen Treiberfehler wurde in der neuen Version behoben*/	
	for (i = 0; i < 10; i++) {
		read(cam_dev, buffer + beyer_offset, imagesize);
		usleep(2000);
	}
	 
	/* Von 10bit Pixel (2 Bytes) zu 8bit Pixels (1Byte) konvertieren*/
	Raw10To8bit(buffer + beyer_offset, imagesize);
	
	
	bgr_buffer = cfa2bgr_DoBayerFiltering(buffer + beyer_offset, sizeof(bmphead_bgr), width, height); 
	free(buffer); 
	
	/* PPI schliessen */
	close(cam_dev);
		
	res_pic.height = height;
	res_pic.width  = width;

	/* 3 Byte pro Pixel alloziieren */
	res_pic.data = (unsigned char*)malloc(imagesize*3);
	res_pic.length = imagesize*3;
	res_pic.type = IMG_BGR;
		
	(void)memcpy(res_pic.data, (unsigned char*)(bgr_buffer + sizeof(bmphead_bgr)), imagesize*3);
	free(bgr_buffer);		
	return res_pic;		
}

lcv_picture lcv_capture_region(const unsigned short start_x, const unsigned short start_y, const unsigned short width, const unsigned short height, const char* filename)
{
	lcv_picture ret_pic;	
	char error_msg[256];
	
	/* Function is not yet fully implemented */
	sprintf(error_msg, "Function not implemented");	
	printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
	lcv_log_error(error_msg, __FUNCTION__);
	exit(-1);
	
	
	/* Lokaler Buffer zur Zwischenspeicherung der Kameraregister 1-4 */
	unsigned short cam_reg_region_buffer[4];
	
	unsigned short nWidth, nHeight, nStart_x, nStart_y;
	
	nWidth   = width;
	nHeight  = height;
	nStart_x = start_x;
	nStart_y = start_y;
	
	/* Auf legale Werte prüfen , Korrektur auf maximal mögliche Fenstergrösse */
	if (nStart_x < cam_reg_config[REG_COLUMN_START].min_val) {
		sprintf(error_msg, "The start_x variable value (%u) is smaller than the minimal legal value: %u", nStart_x , cam_reg_config[REG_COLUMN_START].min_val);		
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1); 
	}
	if (nStart_x > cam_reg_config[REG_COLUMN_START].max_val) {
		sprintf(error_msg, "The start_x variable value (%u) is bigger than the maximal legal value: %u", nStart_x , cam_reg_config[REG_COLUMN_START].max_val);		
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1); 
	}
	
	if (nStart_y < cam_reg_config[REG_ROW_START].min_val) {
		sprintf(error_msg, "The start_y variable value (%u) is smaller than the minimal legal value: %u", nStart_y , cam_reg_config[REG_ROW_START].min_val);		
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1); 
	}
	if (nStart_y > cam_reg_config[REG_ROW_START].max_val) {
		sprintf(error_msg, "The start_y variable value (%u) is bigger than the maximal legal value: %u", nStart_y , cam_reg_config[REG_ROW_START].max_val);		
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1); 

	}
	
	/* Achtung: hier wurden für die Bildausmasse die Default Werte verwendet, die möglichen Werte gehen
	 * über den möglichen Bereich heraus!! */
	if (nWidth > cam_reg_config[REG_WINDOW_WIDTH].max_val) {
		sprintf(error_msg, "The width variable (%u) extends the maximal possible window width in this register configuration", width);		
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1);		
	}
	if (nHeight > cam_reg_config[REG_WINDOW_HEIGHT].max_val) {
		sprintf(error_msg, "The height variable (%u) extends the maximal possible window height in this register configuration", height);		
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1);			
	}
	
	/* Register sichern, erst danach neue Registerwerte schreiben */
	cam_reg_region_buffer[0] = lcv_config_read(REG_COLUMN_START);
	cam_reg_region_buffer[1] = lcv_config_read(REG_ROW_START);
	cam_reg_region_buffer[2] = lcv_config_read(REG_WINDOW_WIDTH);
	cam_reg_region_buffer[3] = lcv_config_read(REG_WINDOW_HEIGHT);
	
		 
	/* Neue, temporäre Registerwerte schreiben */ 
	if (lcv_config_write(REG_COLUMN_START, nStart_x) < 0) {
		sprintf(error_msg, "Could not set a config-register (REG_COLUMN_START)");	
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1);
	}
	if (lcv_config_write(REG_ROW_START, nStart_y) < 0) {
		sprintf(error_msg, "Could not set a config-register (REG_ROW_START)");
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1);
	}
	if (lcv_config_write(REG_WINDOW_WIDTH, nWidth) < 0) {
		sprintf(error_msg, "Could not set a config-register (REG_WINDOW_WIDTH)");
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1);
	}
	if (lcv_config_write(REG_WINDOW_HEIGHT, nHeight) < 0) {
		sprintf(error_msg, "Could not set a config-register (REG_WINDOW_HEIGHT)");
		printf("%s %s:\nError: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_error(error_msg, __FUNCTION__);
		exit(-1);
	}
	
	/* Bild holen */
	ret_pic = lcv_capture_frame("");
	
	/* Register mit dem ursprünglichen Wert wiederherstellen */
	lcv_config_write(REG_COLUMN_START, cam_reg_region_buffer[0]);
	lcv_config_write(REG_ROW_START, cam_reg_region_buffer[1]);
	lcv_config_write(REG_WINDOW_WIDTH, cam_reg_region_buffer[2]);
	lcv_config_write(REG_WINDOW_HEIGHT, cam_reg_region_buffer[3]);
		
	return ret_pic;
}

char lcv_config_write(const unsigned int setting, const unsigned short value)
{		
	char warning_msg[256];
	
	if (0 <= setting && setting < cam_registers_cnt) {
		lcv_reg_config config = cam_reg_config[setting];
		
		if(config.min_val <= value && value <= config.max_val) {
			
			/* mit Register buffering */
			if (enable_buffer == TRUE) {
				/* neuer Registerwert -> schreiben */
				if(cam_registers_buffer[setting] != value) {
					lcv_i2c_write_register(CAM_I2C_DEVICE, CAM_DEV_ID, config.addr, value);
					cam_registers_buffer[setting] = value;
				}
			} else {
				/* Direkt ins Register schreiben */
				lcv_i2c_write_register(CAM_I2C_DEVICE, CAM_DEV_ID, config.addr, value);
			}
			return TRUE;
			
		} else {
			sprintf(warning_msg,"Camera Register Number %u could not be written due to an illegal value: %u\n", config.addr, value);
			lcv_log_warning(warning_msg, __FUNCTION__);			
			return ERROR_ILLEGAL_VALUE;
		}
	} else {
		sprintf(warning_msg,"Camera Register Number %u doesn't exist\n", setting);
		lcv_log_warning(warning_msg, __FUNCTION__);		
		return ERROR_ILLEGAL_SETTING;
	}
}

unsigned short lcv_config_read(const unsigned int setting)
{	
	char error_msg[256];
	
	/* Buffer initialisieren falls noch nicht initialisiert 
	 * aus Performancegründen wird hier der Stand der Initialisierung erneut geprüft */
	if (!cam_registers_buffer_initialized) {
		lcv_cam_registers_buffer_init();
	}
	
	if (0 <= setting && setting < cam_registers_cnt) {
		lcv_reg_config config = cam_reg_config[setting];
		/* mit Register buffering */
		if (enable_buffer == TRUE) {
			return cam_registers_buffer[setting];
		} else {	
			return lcv_i2c_read_register(CAM_I2C_DEVICE, CAM_DEV_ID, config.addr);
		}
	} else {
		sprintf(error_msg,"Camera Register Number %u does not exist", setting);
		//printf("%s %s:\nWarning: %s\n", "In Function", __FUNCTION__, error_msg);
		lcv_log_warning(error_msg, __FUNCTION__);
		return 0;
	}
}

char lcv_regs_to_default()
{
	int i;
	for (i=0; i<cam_registers_cnt; i++) {
		lcv_config_write(i, cam_reg_config[i].default_val);
	}
	return TRUE;
}

void lcv_reset_capture_frame()
{
	lcv_config_write(REG_COLUMN_START, cam_reg_config[REG_COLUMN_START].default_val);
	lcv_config_write(REG_ROW_START, cam_reg_config[REG_ROW_START].default_val);
	lcv_config_write(REG_WINDOW_WIDTH, cam_reg_config[REG_WINDOW_WIDTH].default_val);
	lcv_config_write(REG_WINDOW_HEIGHT, cam_reg_config[REG_WINDOW_HEIGHT].default_val);	
}

void lcv_cam_registers_buffer_init()
{
	int i;
	
	if (!cam_registers_buffer_initialized) {
		for (i=0; i < cam_registers_buffer_cnt; i++) {
			cam_registers_buffer[i] = cam_reg_config[i].default_val;
		}
		cam_registers_buffer_initialized = TRUE;
	}
}

void lcv_reg_buffering(const unsigned char enable) {
	enable_buffer = enable;	
}

